package com.quan.hr.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.quan.commons.core.MyBaseServiceImpl;
import com.quan.commons.utils.DateUtils;
import com.quan.commons.utils.PageUtils;
import io.swagger.models.auth.In;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;

import com.quan.hr.commons.vo.HrCalendarVo;
import com.quan.hr.entity.HrCalendar;
import com.quan.hr.mapper.HrCalendarMapper;
import com.quan.hr.service.HrCalendarService;


@Slf4j
@Service
public class HrCalendarServiceImpl extends MyBaseServiceImpl<HrCalendarMapper, HrCalendar> implements HrCalendarService {

    @Autowired
    private HrCalendarMapper mapper;

    @Override
    public List<HrCalendarVo> queryList(PageUtils pageUtils, HrCalendarVo vo) {
        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>(vo);

        List<HrCalendar> list = super.list(pageUtils, queryWrapper);
        List<HrCalendarVo> collect = list.stream().map(item -> {
                HrCalendarVo bean = new HrCalendarVo();
            BeanUtils.copyProperties(item, bean);
            return bean;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public PageUtils queryPage(PageUtils pageUtils, HrCalendarVo vo) {
        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>(vo);

        if (StringUtils.isNotBlank(vo.getKeyword())) {
            queryWrapper.and(w -> {
                w.like("name", vo.getKeyword());
            });
        }

        final IPage<HrCalendar> page = super.selectPage(pageUtils, queryWrapper);

        PageUtils ps = new PageUtils(page);

        final List<HrCalendar> records = page.getRecords();

        final List<HrCalendar> collect = records.stream().map((item) -> {
                HrCalendarVo bean = new HrCalendarVo();
            BeanUtils.copyProperties(item, bean);
            return bean;
        }).collect(Collectors.toList());

        ps.setRecords(collect);
        return ps;
    }

    /**
     * 获取某年某月日历
     * 前端是7X6表格，有42个格子，需要获取当月的上一个月的数据和下一个月的数据来填充忙42个格子
     * @param year
     * @param month
     * @return
     */
    @Override
    public List<HrCalendar> getList(String year, String month) {

        Calendar calendar = Calendar.getInstance();
        // 补齐上月数据填充到表格
        Calendar start = Calendar.getInstance();
        // 获取当月的1号是周几
        calendar.set(Integer.parseInt(year), Integer.parseInt(month)-1, 1);
        int week = calendar.get(Calendar.DAY_OF_WEEK);

        // week=1，（7-week）为周日，需显示上月6天数据，填充空白表格
        // week=2为周一，无需显示上月
        // week>2 & week<=7 (week-1)为周二至周六，需显示上月，填充空白表格
        if(week == 1) {
            week = 7-week;
        } else if(week == 2) {
            week = 0;
        } else if(week > 2 || week <= 7) {
            week = week - 2;
        }

        start.set(Integer.parseInt(year), Integer.parseInt(month)-1, 0);
        // 获取月份的最后一天，用于迭代月份的每一天
        final int maxDay = start.getActualMaximum(Calendar.DAY_OF_MONTH);
        int day = (maxDay - week);
        start.set(Integer.parseInt(year), Integer.parseInt(month)-2, day);
        log.info("上月补齐开始日期：{}" ,DateUtils.formatDate(start.getTime()));
        //--------------------------------上月补齐开始日期---------------------------------------------//

        calendar.set(Integer.parseInt(year), Integer.parseInt(month), 0);
        // 获取月份的最后一天，用于迭代月份的没一天
        final int endMaxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        Calendar end = Calendar.getInstance();
        int endDay = 42 - (week + endMaxDay);
        end.set(Integer.parseInt(year), Integer.parseInt(month, day), endDay);

        log.info("下月补齐结束日期：{}" ,DateUtils.formatDate(end.getTime()));
        //--------------------------------下月补齐结束日期---------------------------------------------//

        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>();

        //queryWrapper.apply("date_format(calendar_date, '%Y-%m-%d') >= {0} and date_format(calendar_date, '%Y-%m-%d') <= {1}", start.getTime(), end.getTime());
        queryWrapper.between("calendar_date", start.getTime(), end.getTime());

        return super.list(queryWrapper);
    }

    /**
     * 获取指定年的每月的工作天数
     * @param year
     * @return
     */
    @Override
    public Map<Integer, Long> getYearCalendarWorkingCount(Integer year) {
        Map<Integer, Long> map = new HashMap<Integer, Long>();
        List<Long> list = new ArrayList<Long>();
        for (int i = 1; i <= 12; i++) {
            String d = year + "-" + (i < 10 ? "0" + i : i);

            QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>();

            queryWrapper.apply("date_format(calendar_date, '%Y-%m') = {0}", d);
            queryWrapper.eq("holiday", 0);
            queryWrapper.and(w -> {
                w.eq("weekend", 0).or().eq("weekend", 2);
            });

            final long count = super.count(queryWrapper);

            map.put(i, count);
            list.add(count);
        }

        return map;
    }

    /**
     * 查询某个范围的日历（包含周末，节假日）
     * @param start
     * @param end
     * @return
     */
    @Override
    public List<HrCalendar> getBetweenCalendar(String start, String end) {
        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>();

        queryWrapper.between("calendar_date", start, end);

        return super.list(queryWrapper);
    }

    /**
     * 查询某个范围的日历，实际工作天数（不包含周末，节假日）
     * @param start
     * @param end
     * @return
     */
    @Override
    public List<HrCalendar> getBetweenCalendarWorking(String start, String end) {

        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>();

        queryWrapper.between("calendar_date", start, end);
        queryWrapper.eq("holiday", 0);
        queryWrapper.and(w -> {
            w.eq("weekend", 0).or().eq("weekend", 2);
        });

        return super.list(queryWrapper);
    }

    /**
     * 统计某个范围的日历，实际工作天数（不计算周末和节假日）
     * @param start
     * @param end
     * @return
     */
    public long getBetweenCalendarWorkingCount(String start, String end) {

        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>();

        queryWrapper.between("calendar_date", start, end);
        queryWrapper.eq("holiday", 0);
        queryWrapper.and(w -> {
            w.eq("weekend", 0).or().eq("weekend", 2);
        });

        return super.count(queryWrapper);
    }

    /**
     * 统计某个范围的日历，实际工作天数（不计算周末和节假日）
     * @param start
     * @param end
     * @return
     */
    @Override
    public long getBetweenCalendarWorkingCount(LocalDate start, LocalDate end) {
        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>();

        queryWrapper.between("calendar_date", start, end);
        queryWrapper.eq("holiday", 0);
        queryWrapper.and(w -> {
            w.eq("weekend", 0).or().eq("weekend", 2);
        });

        return super.count(queryWrapper);
    }

    /**
     * 获取月的工作天数
     * @param year
     * @param month
     * @return
     */
    @Override
    public long getWorkingCount(int year, int month) {
        // 本月的一号
        Calendar s = Calendar.getInstance();
        s.set(year, month - 1, 1);

        // 本月的最后一天
        Calendar e = Calendar.getInstance();
        e.set(year, month - 1, s.getActualMaximum(Calendar.DAY_OF_MONTH));

        QueryWrapper<HrCalendar> queryWrapper = new QueryWrapper<HrCalendar>();

        queryWrapper.between("calendar_date", DateUtils.formatDate(s.getTime()), DateUtils.formatDate(e.getTime()));
        queryWrapper.eq("holiday", 0);
        queryWrapper.and(w -> {
            w.eq("weekend", 0).or().eq("weekend", 2);
        });

        return super.count(queryWrapper);
    }

    /**
     * 设置节假日
     * @param vo
     */
    @Override
    public void setHoliday(HrCalendarVo vo) {

        LambdaUpdateWrapper<HrCalendar> wrapper = new UpdateWrapper().lambda();

        wrapper.between(HrCalendar::getCalendarDate, vo.getStartDate(), vo.getEndDate());

        wrapper.set(HrCalendar::getCalendarTitle, vo.getCalendarTitle());
        wrapper.set(HrCalendar::getWeekend, vo.getWeekend());
        wrapper.set(HrCalendar::getHoliday, vo.getHoliday());
        wrapper.set(HrCalendar::getRemarks, vo.getRemarks());

        super.update(wrapper);
    }


    /**
     * 初始化日历，指定年
     * @param year
     */
    @Override
    public void initCalendar(Integer year) {
        List<HrCalendar> list = new ArrayList<>();
        for (int month = 1; month <= 12; ++month) {
            list.addAll(initCalendarList(year, month));
        }

        super.removeBatchByIds(list);
        super.saveBatch(list);
    }

    /**
     * 初始化日历，指定年月
     * @param year
     * @param month
     */
    @Override
    public void initCalendar(Integer year, Integer month) {
        final List<HrCalendar> list = initCalendarList(year, month);

        super.removeBatchByIds(list);

        super.saveBatch(list);

    }

    /**
     * 初始化日历
     * @param year
     * @param month
     * @return
     */
    private static List<HrCalendar> initCalendarList(int year, int month) {
        List<HrCalendar> list = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();

        // 获取月份的最后一天，用于迭代月份的每一天
        calendar.set(year, month, 0);
        final int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

        for (int day = 1; day <= maxDay ; day++) {
            calendar.set(year, month - 1, day);
            int week = calendar.get(Calendar.DAY_OF_WEEK);

            HrCalendar entity = new HrCalendar();
            entity.setCalendarDate(DateUtils.UDateToLocalDate(calendar.getTime()));
            entity.setCalendarTitle(getWeek(calendar));
            entity.setWeekend(week == 1 || week == 7 ? 1 : 0 );

            list.add(entity);
        }
        return list;
    }


    /**
     * 初始化日历的节假日
     * @param year
     * @param month
     * @return
     */
    public Map<String, Map<String, Object>> initCalendarHoliday(Integer year, Integer month) {

        final Map<String, Map<String, Object>> holiday = getHoliday(year, month);;

        if(!holiday.isEmpty()) {
            Set<String> strings = holiday.keySet();
            for (String str : strings) {
                Map<String, Object> stringObjectMap = holiday.get(str);

                // 日期
                String date = (String) stringObjectMap.get("date");
                // 节假日
                String name = (String) stringObjectMap.get("name");
                // 是否假期【true：放假】
                Boolean holidy = (Boolean) stringObjectMap.get("holiday");
                // 补班
                Boolean after = (Boolean) stringObjectMap.get("after");
                // 节假日统称
                String target = (String) stringObjectMap.get("target");

                final HrCalendar hrCalendar = super.getById(date);
                if(holidy) {
                    hrCalendar.setCalendarTitle(name);
                    hrCalendar.setHoliday(1);
                }

                if(null != after && after) {
                    hrCalendar.setRemarks(name);
                    hrCalendar.setWeekend(2);
                }

                super.updateById(hrCalendar);

            }
        }

        return holiday;
    }

    /**
     * 获取国家节假日
     * @return
     */
    private static Map<String, Map<String, Object>> getHoliday(Integer year, Integer month) {

        String date = null != month ? year+"-"+month : year.toString();

        String url = "http://timor.tech/api/holiday/year/"+date+"/";

        final String res = HttpUtil.get(url);
        final Map map = JSONObject.parseObject(res, LinkedHashMap.class);

        Integer code = (Integer) map.get("code");
        if (code != 0) {
            return null;
        }

        return (Map<String, Map<String, Object>>) map.get("holiday");
    }


    private static String getWeek(Calendar date) {
        final String dayNames[] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
        int dayOfWeek = date.get(Calendar.DAY_OF_WEEK);
        return dayNames[dayOfWeek - 1];
    }
}